#include <string.h>

#include "adc.h"

#undef TRACE_LEVEL
#undef TRACE_TITLE
#undef TRACE_TITLE
#define TRACE_LEVEL	TRACE_LEVEL_DEBUG
#define TRACE_NAME  TRACE_NAME_DISABLE
#define TRACE_TITLE "ADC"
#include "trace.h"

/* ADC Cache buffer length in half-word(16BIT) */
#define ADC_DMA_CACHE_SIZEHW	64

/* ADC Cache buffer */
// static __IO uint16_t ADC_DMA_CACHE[3 * ADC_DMA_CACHE_SIZEHW];
static __IO uint16_t ADC_DMA_CACHE[2 * ADC_DMA_CACHE_SIZEHW];
// static __IO uint16_t X, Y, Z;
static int ADC_DMA_COMPLETE	=	0;

static void ADC_GPIO_Config(void);
static void ADC_DMA_Config(void *DMA_Cache_Base, int cacheSizeByte);
static void ADC_Basic_Config(ADC_TypeDef* ADCx);


#define SENSOR_CHANNEL_ADC		ADC1



int OpenBatteryMonitorADC() {
	return 0;
}

void CloseBatteryMonitorADC() {
	
}

int ReadBatteryMonitorADC() {
	return 0;
}





/*
** @ Open ADC
**/
int ADC_Open(ADC_TypeDef* ADCx) {	
	/* Confi ADC input Pins */
	// ADC_GPIO_Config();
	
	/* Config the DMA for ADC */
	ADC_DMA_Config((void *)ADC_DMA_CACHE, sizeof(ADC_DMA_CACHE));
	
	/* Config the ADCx */
	ADC_Basic_Config(ADCx);

// 	X = 0; Y = 0; Z = 0;
	return 0;
}

void ADC_SoftwareStart(ADC_TypeDef* ADCx) {
	/* Start ADC1 Software Conversion */ 
	ADC_DMA_COMPLETE = 0;
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, ENABLE);
	while (ADC_DMA_COMPLETE == 0);
	
	ADC_DMA_COMPLETE = 0;
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, ENABLE);
	while (ADC_DMA_COMPLETE == 0);
}

/*
** @ ADC Read three channels value
**/
// void ADC_Read(uint16_t *x, uint16_t *y, uint16_t *z) {
void ADC_Read(int channel, uint16_t *x, uint16_t *y) {
	uint32_t xSum = 0;
	uint32_t ySum = 0;
	// uint32_t zSum = 0;
	int i;
	
	// for( i = 0; i < (ADC_DMA_CACHE_SIZEHW * 3); i += 3) {
	for( i = 0; i < (ADC_DMA_CACHE_SIZEHW * 2); i += 2) {
		xSum += ADC_DMA_CACHE[i];
		ySum += ADC_DMA_CACHE[i + 1];
		// zSum += ADC_DMA_CACHE[i + 2];
	}
	
	*x = xSum >> 6;
	*y = ySum >> 6;
	// *z = zSum >> 6;
}

/*
** @ Close the ADC
**/
int ADC_Close(ADC_TypeDef* ADCx) {
	/* Deinit the ADC Pins */
	GPIO_InitTypeDef GPIO_InitStructure;
	
	DMA_DeInit(ADC_DMA_Channel);
	ADC_DeInit(ADCx);

	/* Configure ADC Channels as floating input */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	
	GPIO_InitStructure.GPIO_Pin = ADC_X_PIN;
	GPIO_Init(ADC_X_GPIO, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = ADC_Y_PIN;
	GPIO_Init(ADC_Y_GPIO, &GPIO_InitStructure);
	
	// GPIO_InitStructure.GPIO_Pin = ADC_Z_PIN;
	// GPIO_Init(ADC_Z_GPIO, &GPIO_InitStructure);
	
	return 0;
}

/* config GPIO for ADC input */
static void _ADC_GPIO_Config(GPIO_TypeDef *GPIOx, uint16_t ADC_Pins_Mark) {
	GPIO_InitTypeDef GPIO_InitStructure;

	/* Configure ADC Channels as analog input */
	GPIO_InitStructure.GPIO_Pin = ADC_Pins_Mark;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOx, &GPIO_InitStructure);
}

// static void _OUTPUT_GPIO_Config(GPIO_TypeDef *GPIOx, uint16_t ADC_Pins_Mark) {
// 	GPIO_InitTypeDef GPIO_InitStructure;

// 	/* Configure ADC Channels as analog input */
// 	GPIO_InitStructure.GPIO_Pin = ADC_Pins_Mark;
// 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 	GPIO_Init(GPIOx, &GPIO_InitStructure);
// 	
// 	GPIO_SetBits(GPIOx, ADC_Pins_Mark);
// }

/* Config GPIOs input for ADC */
static void ADC_GPIO_Config() {
	_ADC_GPIO_Config(ADC_X_GPIO, ADC_X_PIN);
	_ADC_GPIO_Config(ADC_Y_GPIO, ADC_Y_PIN);
	// _ADC_GPIO_Config(ADC_Z_GPIO, ADC_Z_PIN);
	// _OUTPUT_GPIO_Config(ADC_Z_GPIO, ADC_Z_PIN);
}

/* ADC Basic Config */
static void ADC_Basic_Config(ADC_TypeDef* ADCx) {
	ADC_InitTypeDef ADC_InitStructure;
	
	/* ADC1 configuration ------------------------------------------------------*/
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	// ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	// ADC_InitStructure.ADC_NbrOfChannel = 3;
	ADC_InitStructure.ADC_NbrOfChannel = 2;
	ADC_Init(ADC1, &ADC_InitStructure);
	
	/* ADC1 regular channels configuration */ 
	ADC_RegularChannelConfig(ADC1, ADC_X_CHANNEL, 1, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Y_CHANNEL, 2, ADC_SampleTime_55Cycles5); 
	// ADC_RegularChannelConfig(ADC1, ADC_Z_CHANNEL, 3, ADC_SampleTime_55Cycles5); 
	
	/* Enable ADC1 DMA */
	ADC_DMACmd(ADC1, ENABLE);
	
	/* Enable ADC1 */
	ADC_Cmd(ADC1, ENABLE);

	/* Enable ADC1 reset calibration register */   
	ADC_ResetCalibration(ADC1);
	/* Check the end of ADC1 reset calibration register */
	while(ADC_GetResetCalibrationStatus(ADC1));

	/* Start ADC1 calibration */
	ADC_StartCalibration(ADC1);
	/* Check the end of ADC1 calibration */
	while(ADC_GetCalibrationStatus(ADC1));
	
	/* Start ADC1 Software Conversion */ 
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

static void ConfigDMA4ADC1(void *DMACache, int DMACacheSizeInByte) {
	
}

/* DMA Config for ADC */
static void ADC_DMA_Config(void *DMA_Cache_Base, int cacheSizeByte) {
#define ADC1_DR_Address    ((uint32_t)0x4001244C)
	DMA_InitTypeDef DMA_InitStructure;
	
	/* DMA1 channel1 configuration ----------------------------------------------*/
	DMA_DeInit(ADC_DMA_Channel);
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
	/* buffer base address */
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMA_Cache_Base;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	/* buffer length in half-word */
	DMA_InitStructure.DMA_BufferSize = cacheSizeByte / sizeof(uint16_t);
	
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	DMA_Init(ADC_DMA_Channel, &DMA_InitStructure);  
	
	/* Enable DMA1 channel1 */
	// DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, ENABLE);
	DMA_Cmd(ADC_DMA_Channel, ENABLE);
}

/*
** @ DMA Transfer Done Callback
**/
void ADC_DMA_Done_Callback(void) {
	ADC_DMA_COMPLETE = 1;
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, DISABLE);
#if (0)
	uint32_t xSum = 0;
	uint32_t ySum = 0;
	uint32_t zSum = 0;
	int i;
	
	for( i = 0; i < (ADC_DMA_CACHE_SIZEHW * 3); i += 3) {
		xSum += ADC_DMA_CACHE[i];
		ySum += ADC_DMA_CACHE[i + 1];
		zSum += ADC_DMA_CACHE[i + 2];
	}
	
	X = xSum >> 6;
	Y = ySum >> 6;
	Z = zSum >> 6;
#endif
}

// TEST ONLY ****** TEST ONLY ****** TEST ONLY ****** TEST ONLY ****** TEST ONLY ******
#include "tick.h"
#include "usart.h"

int ADC_Main() {
	ADC_Open(ADC1);	
	
	while(1) ;
// 	{
// 		USART_Putc(USART3, 0x0C);
// 		TRACE_DEBUG_WP("X %d\r\n", ADC1ConvertedValue[0]);
// 		TRACE_DEBUG_WP("Y %d\r\n", ADC1ConvertedValue[1]);
// 		TRACE_DEBUG_WP("Z %d\r\n", ADC1ConvertedValue[2]);
// 		delay_ms(200);
// 	}
}
